07|行级锁功过

MySQL行级别由各个存储引擎实现,但是有些存储引擎不支持行级锁,例如MyISAM,所以只能使用表级锁。所以在并发控制的粒度上更粗一些。InnoDB是支持行级锁的,这也是InnoDB取代MyISAM的重要原因

两阶段锁

事务A 事务B
begin;update t set k=k+1 where id=1;update t set k= k+1 where id = 2 没有
没有 begin;update t set k=k+2 where id = 1
commit 没有
  • 事务A未执行完毕之前,事务B的操作会被锁住,事务A持有了id=1和id=2的行级锁。所以尽量把持有多个行的锁的事务操作放在最后,使其影响最小。

案例

  1. 顾客A余额扣除电影票价
  2. 影院B余额增加电影票价
  3. 记录一条交易日志

问题:如何调整顺序,使其影响最小

  • 三个操作都是原子性操作,放在一个事务里面
  • 另一个顾客买票时,有事务冲突的就是2,需要修改同一条记录数据。
  • 按照3、1、2的顺序,语句2的锁时间最小,减少了事务之间的锁等待,提高了并发度

死锁与死锁监测

当并发系统中不同线程出现循环资源依赖。涉及的线程就回等待别的线程释放资源,就回导致几个线程进入无线等待的状态,从而导致死锁。

事务A 事务B
begin;update t set k=k+1 where id =1 begin;
没有 update t set k=k+1 where id = 2
update t set k = k+1 where id = 2 没有
没有 update t set k=k+1 where id = 1
  • 事务A在等待事务B中id=2的行锁
  • 事务B在等待事务A中id=1的行锁

如何解决死锁问题

  1. 设置超时时间(innodb_lock_wait_timeout)
  • 默认50s,第一个被锁住的线程在50s会超时退出。等待时间太长,对于在线服务不能容忍。
  • 设置超时时间1s,如果只是简单的锁等待,容易造成误伤。
  1. 死锁检测

    每当一个事务被所得时候,就要看它所依赖的线程有没有被人锁住,最后判断你是否出现了循环等待。

死锁监测方案:

  • 如果所有事务都更新同一行记录,死锁监测的成本非常高,会导致性能问题。如1000个并发线程同时更新同一条记录,死锁监测的操作就是100万级别。这期间会消耗大量的CPU资源,效率很低。
  • 关闭死锁监测,由业务上来保证不出现死锁。关掉死锁监测有一定的风险,当出现超时的时候,对业务是有损的。
  • 控制并发度,使其死锁检测成本变低。当客户端很多的时候,及时每个客户端只有五个并发线程,峰值并发也可能达到3000
  • 从设计上解决。将数据库的一行操作改成逻辑上的多行操作来减少锁冲突。如影院的余额设置在多条记录上,每次给应选加金额的时候,随机选一条来加,由此冲突概率将变成原来的1/10。